package file

import (
	"fmt"
	"gitee.com/huanminabc/go-utils/cmd"
	"gitee.com/huanminabc/go-utils/common"
	"gitee.com/huanminabc/go-utils/container"
	"gitee.com/huanminabc/go-utils/system"
	"gitee.com/huanminabc/go-utils/vars"
	"os"
	"runtime/debug"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

// 实现一个文件查看器
type FileViewer struct {
	//文件路径
	filePath            string
	originalFileContent []string //原始文件内容没有任何修改修饰的,目前就只有在非实时监控的时候才会有
	//文件内容
	fileContent []string

	//=========================================监控相关=========================================
	//是否实时监控
	isMonitor     bool
	closeChan     chan bool
	inputChan     chan string
	pause         atomic.Bool
	prevPageLines int //向上翻页的计数
	pageLimit     int //翻页限制 默认5
	temp          *container.Queue[string]
	searchKey     string //搜索关键字
	searchIndex   []int  //搜索索引
	currIndex     int    //当前索引
	lineNumLimit  int    //每页展示的行数
	lineNum       int    //当前行数

	addMonitorFunc func(string []string) //在监控模式下,文件发生变化时的回调函数,只是增量情况下 ,如果需要可以配置
	//=========================================文件查看和编辑相关=========================================
	//参数
	params       string
	editPrintOff bool //编辑模式下是否关闭打印, 因为如果是别人调用,那么就不需要打印, 默认是打印的
}

// 创建一个文件查看器
func NewFileViewer(filePath string, isMonitor bool, params string) *FileViewer {
	var pageLimit = 5
	//参数去空
	return &FileViewer{
		filePath:      filePath,
		params:        params,
		closeChan:     make(chan bool),
		inputChan:     make(chan string),
		isMonitor:     isMonitor,
		pause:         atomic.Bool{},
		pageLimit:     pageLimit,
		prevPageLines: 0,

		temp:         container.NewQueue[string](),
		lineNumLimit: 100,
		lineNum:      0,
		editPrintOff: true,
	}
}

// 添加回调函数
func (f *FileViewer) AddMonitorFunc(addMonitorFunc func(string []string)) {
	f.addMonitorFunc = addMonitorFunc

}

// 是否开启编辑模式打印,不打印false
func (f *FileViewer) SetEditPrintOff(editPrintOff bool) {
	f.editPrintOff = editPrintOff
}

// 启动文件查看器
func (f *FileViewer) Start() []string {
	//读取文件内容
	f.ReadFileContent()
	//输出文件内容
	f.PrintContent()

	//如果是开启实时监控
	if f.isMonitor {

		//交互
		f.Interactive()
		<-f.closeChan
		//fmt.Println("文件查看器已经停止")
	}
	return f.originalFileContent

}

// 停止文件查看器
func (f *FileViewer) Stop() {
	f.closeChan <- true
	close(f.closeChan) //关闭通道
}

// 读取文件内容
func (f *FileViewer) ReadFileContent() {
	if f.isMonitor {
		go func() {

			ReadFileMonitor(f.filePath, f.lineNumLimit, f.closeChan, func(line string, lineNum int, size int64, time2 time.Time, mode int) {

				//如果f.pause为true,则暂停
				if f.pause.Load() {
					f.temp.PushTail(line)
					return
				}
				lines := strings.Split(line, "\n")

				if mode == -1 { //第一次读取文件
					f.prevPageLines = lineNum
					f.lineNum = lineNum
					var strBuilder strings.Builder
					var startIndex = 1
					if f.lineNum > f.lineNumLimit {
						startIndex = f.lineNum - f.lineNumLimit
					}
					for i := range lines {
						strBuilder.WriteString(fmt.Sprintf("%s %s\n", vars.BlueColor(startIndex+i), lines[i]))
					}
					f.inputChan <- strBuilder.String() // 通知输出
					return
				}
				if mode == 1 { //有人修改了文件
					//先清屏
					f.ClearScreen()
					//清除内容
					f.fileContent = []string{}
					//设置行数
					f.lineNum = lineNum
					//重置行数
					f.prevPageLines = lineNum
					//清除搜索
					f.searchKey = ""
					f.searchIndex = []int{}

					var startIndex = 1
					if f.lineNum > f.lineNumLimit {
						startIndex = f.lineNum - f.lineNumLimit
					}
					//设置行号
					for i := range lines {
						lines[i] = fmt.Sprintf("%s %s", vars.BlueColor(startIndex+i), lines[i])
					}
					f.inputChan <- strings.Join(lines, "\n") // 通知输出
					return
				}

				if mode == 2 { //新增内容
					if f.addMonitorFunc != nil {
						f.addMonitorFunc(lines) //回调函数,传入增量内容
					}
					//设置行号
					for i := range lines {
						lines[i] = fmt.Sprintf("%s %s", vars.BlueColor(f.lineNum+i), lines[i])
					}
					f.lineNum = lineNum

					join := strings.Join(lines, "\n")
					f.inputChan <- join // 通知输出
				}
			})
		}()
	} else {
		var preNum = 1
		if strings.HasPrefix(f.params, "h") && len(f.params) == 1 {
			fmt.Println("================帮助文档================")
			fmt.Println("h1:显示文件头部1行,h2:显示文件头部2行")
			fmt.Println("t1:显示文件尾部1行,t2:显示文件尾部2行")
			fmt.Println("g1:显示文件第1行,g2:显示文件第2行")
			fmt.Println("g1~2:显示文件第1行到第2行")
			fmt.Println("addh:在文件头部添加内容,例如:addh hello")
			fmt.Println("addt:在文件尾部添加内容,例如:addt hello")
			fmt.Println("add1 行内容:在文件第1行的下面插入新内容,例如:add1 hello")
			fmt.Println("cover1 行内容:在文件第1行覆盖新内容,例如:cover1 hello")
			fmt.Println("del1:删除文件第1行,例如:del1")
			fmt.Println("del1-2:删除文件第1行到第2行,例如:del1-2")
			fmt.Println("rep1-2 /内容/新内容/:替换文件第1行到第2行的内容,例如:rep1-2 /hello/world/")
			fmt.Println("rep /内容/新内容/:替换文件所有的内容,例如:rep /hello/world/")
			fmt.Println("rep1 /内容/新内容/:替换文件第1行的内容,例如:rep1 /hello/world/")
			fmt.Println("======================================")
			return
		}

		//params 如果是h1,则只显示头部1行 如果是t1,则只显示尾部1行
		if strings.HasPrefix(f.params, "h") && len(f.params) > 1 {
			atoi, err := strconv.Atoi(f.params[1:])
			if err != nil {
				panic(err)
			}
			file := ReadFileHeadRange(f.filePath, atoi)
			split := strings.Split(file, "\n")
			f.fileContent = split
		} else if strings.HasPrefix(f.params, "t") && len(f.params) > 1 {
			atoi, err := strconv.Atoi(f.params[1:])
			if err != nil {
				panic(err)
			}

			preNum = GetFileLine(f.filePath) - atoi + 1
			file := ReadFileTailRange(f.filePath, atoi)
			split := strings.Split(file, "\n")
			f.fileContent = split
		} else if strings.HasPrefix(f.params, "g") && len(f.params) > 1 {
			//判断是否包含-
			if strings.Contains(f.params, "-") {
				//获取指定行数
				atoi, err := strconv.Atoi(f.params[1:strings.Index(f.params, "-")])
				if err != nil {
					panic(err)
				}
				atoi2, err := strconv.Atoi(f.params[strings.Index(f.params, "-")+1:])
				if err != nil {
					panic(err)
				}
				file := ReadFileLineNum(f.filePath, atoi, atoi2)
				split := strings.Split(file, "\n")
				preNum = atoi
				f.fileContent = split
			} else {
				//获取指定行数
				atoi, err := strconv.Atoi(f.params[1:])
				if err != nil {
					panic(err)
				}
				file := ReadFileLineNum(f.filePath, atoi, atoi)
				split := strings.Split(file, "\n")
				preNum = atoi
				f.fileContent = split
			}
		} else if strings.HasPrefix(f.params, "add") && len(f.params) > 3 {
			//判断是addh
			if strings.HasPrefix(f.params, "addh") {
				str := f.params[4:]
				str = strings.TrimSpace(str)
				file := ReadFile(f.filePath)
				str = fmt.Sprintf("%s\n%s", str, file)
				split := strings.Split(str, "\n")
				WriteSliceFile(f.filePath, split)
				f.fileContent = split
			} else if strings.HasPrefix(f.params, "addt") {
				str := f.params[4:]
				str = strings.TrimSpace(str)
				file := ReadFile(f.filePath)
				//去掉最后一个换行符,后面会自行添加
				file = strings.TrimRight(file, vars.SystemLineBreak)
				str = fmt.Sprintf("%s\n%s", file, str)
				split := strings.Split(str, "\n")
				WriteSliceFile(f.filePath, split)
				f.fileContent = split
			} else {
				index := strings.Index(f.params, " ")
				//获取指定行数
				atoi, err := strconv.Atoi(f.params[3:index])
				if err != nil {
					panic(err)
				}
				file := ReadFile(f.filePath)
				//获取指定行数
				if atoi > len(file) {
					atoi = len(file)
				}
				str := f.params[index+1:]
				str = strings.TrimSpace(str)
				//插入到指定行数
				split := strings.Split(file, "\n")
				common.InsertSlice(&split, atoi, str)
				WriteSliceFile(f.filePath, split)
				f.fileContent = split
			}

		} else if strings.HasPrefix(f.params, "cover") && len(f.params) > 5 {
			index := strings.Index(f.params, " ")
			//获取指定行数
			atoi, err := strconv.Atoi(f.params[5:index])
			if err != nil {
				panic(err)
			}
			file := ReadFile(f.filePath)
			//获取指定行数
			if atoi > len(file) {
				atoi = len(file)
			}
			str := f.params[index+1:]
			str = strings.TrimSpace(str)
			//覆盖到指定行数
			split := strings.Split(file, "\n")
			split[atoi-1] = str
			WriteSliceFile(f.filePath, split)
			f.fileContent = split
		} else if strings.HasPrefix(f.params, "del") && len(f.params) > 3 {
			//判断是否有~
			if strings.Contains(f.params, "-") {
				//获取指定行数
				atoi, err := strconv.Atoi(f.params[3:strings.Index(f.params, "-")])
				if err != nil {
					panic(err)
				}
				atoi2, err := strconv.Atoi(f.params[strings.Index(f.params, "-")+1:])
				if err != nil {
					panic(err)
				}
				file := ReadFile(f.filePath)
				//获取指定行数
				if atoi > len(file) {
					atoi = len(file)
				}
				if atoi2 > len(file) {
					atoi2 = len(file)
				}
				//删除指定行数
				split := strings.Split(file, "\n")
				common.DelSliceRange(&split, atoi-1, atoi2-1)
				//删除文件
				DeleteFile(f.filePath)
				//合并内容
				WriteSliceFile(f.filePath, split)
				f.fileContent = split
			} else {
				//获取指定行数
				atoi, err := strconv.Atoi(f.params[3:])
				if err != nil {
					panic(err)
				}
				file := ReadFile(f.filePath)
				//获取指定行数
				if atoi > len(file) {
					atoi = len(file)
				}
				//删除指定行数
				split := strings.Split(file, "\n")
				common.DelSlice(&split, atoi-1)
				//删除文件
				DeleteFile(f.filePath)
				WriteSliceFile(f.filePath, split)
				f.fileContent = split
			}

		} else if strings.HasPrefix(f.params, "rep") {
			index := strings.Index(f.params, " ")
			str := f.params[index+1:]
			//按照/分割
			split := strings.Split(str, "/")
			//排除空
			common.SliceRemoveEmpty(&split)
			//替换
			//获取第一个
			restr := split[0]
			restr = strings.TrimSpace(restr)
			//获取第二个
			restr2 := split[1]
			restr2 = strings.TrimSpace(restr2)
			//获取指定行数
			num := f.params[3:index]
			//判断是否是数字
			if common.IsInt(num) {
				atoi, err := strconv.Atoi(num)
				if err != nil {
					panic(err)
				}
				file := ReadFile(f.filePath)
				//获取指定行数
				if atoi > len(file) {
					atoi = len(file)
				}
				//替换指定行数
				split_ := strings.Split(file, "\n")
				split_[atoi-1] = strings.ReplaceAll(split_[atoi-1], restr, restr2)
				WriteStrFile(f.filePath, strings.Join(split_, "\n"))
				f.fileContent = split_
			} else {
				//替换全部
				readfile := ReadFile(f.filePath)
				readfile = strings.ReplaceAll(readfile, restr, restr2)
				WriteStrFile(f.filePath, readfile)
				f.fileContent = strings.Split(readfile, "\n")
			}

		} else {
			file := ReadFile(f.filePath)
			f.fileContent = strings.Split(file, "\n")
		}
		//合并内容让后判空
		join := strings.Join(f.fileContent, "")
		join = strings.TrimSpace(join)
		if join == "" {
			return
		}
		f.originalFileContent = f.fileContent //原始文件内容
		//行号添加
		for i := range f.fileContent {
			f.fileContent[i] = fmt.Sprintf("%s %s", vars.BlueColor(preNum+i), f.fileContent[i])
		}

	}
}

// 内容输出到标准输出
func (f *FileViewer) PrintContent() {
	if f.editPrintOff {
		join := strings.Join(f.fileContent, "\n")
		f.PrintStdout(join)
	}
	//实时监控
	if f.isMonitor {
		go func() {
			for {
				select {
				case <-f.closeChan:
					return
				case line := <-f.inputChan:
					f.fileContent = append(f.fileContent, strings.Split(line, "\n")...)
					//如果行数大于12w则排除前面的行
					if len(f.fileContent) > 120000 {
						f.fileContent = f.fileContent[20000:] //排除前面的20000行
					}
					f.PrintStdout(line)
				}
			}
		}()
	}
}

// 输出到标准输出
func (f *FileViewer) PrintStdout(str string) {
	_, err := os.Stdout.WriteString(str + "\n")
	if err != nil {
		panic(err)
		return
	}

}

// 向上翻页,一次翻页10行, 原理就是不要输出后10行
func (f *FileViewer) PrevPage() {
	//按行分页
	if f.prevPageLines > f.pageLimit { //最少展示f.pageLimit行
		f.ClearScreen()
		f.prevPageLines -= f.pageLimit
		//判断f.prevPageLines是否小于0
		if f.prevPageLines < 0 {
			f.prevPageLines = f.pageLimit
		}
		newShow := f.fileContent[:f.prevPageLines]
		//因为显示屏幕没有这么大,所以只显示f.lineNumLimit行
		if len(newShow) > f.lineNumLimit {
			newShow = newShow[len(newShow)-f.lineNumLimit:]
		}
		join := strings.Join(newShow, "\n")
		f.PrintStdout(join)
	}

}

// 向下翻页,一次翻页10行, 原理就是回复向上翻页的内容
func (f *FileViewer) NextPage() {
	//按行分页
	//截取前面的内容
	if f.prevPageLines <= len(f.fileContent) {
		f.ClearScreen()
		f.prevPageLines += f.pageLimit
		//判断f.prevPageLines是否大于len(f.fileContent)
		if f.prevPageLines > len(f.fileContent) {
			f.prevPageLines = len(f.fileContent)
		}
		newShow := f.fileContent[:f.prevPageLines]
		//因为显示屏幕没有这么大,所以只显示f.lineNumLimit行
		if len(newShow) > f.lineNumLimit {
			newShow = newShow[len(newShow)-f.lineNumLimit:]
		}
		join := strings.Join(newShow, "\n")
		f.PrintStdout(join)
	}
}

// 清屏
func (f *FileViewer) ClearScreen() {
	system.ClearScreen()
}

// 搜索
func (f *FileViewer) Search(search string) {
	key := strings.TrimSpace(search)
	//判断是否为空
	if key == "" {
		return

	}
	//清空
	f.searchKey = key
	f.searchIndex = []int{}
	//遍历,找到所有的索引
	for i := range f.fileContent {
		if strings.Contains(f.fileContent[i], key) {
			//给内容加上颜色
			f.fileContent[i] = strings.ReplaceAll(f.fileContent[i], key, vars.RedColor(key))
			f.searchIndex = append(f.searchIndex, i)
		}
	}
	//设置当前索引为最后一个
	if len(f.searchIndex) > 0 {
		f.currIndex = f.searchIndex[len(f.searchIndex)-1]
		start := f.currIndex
		//设置翻页行数
		f.prevPageLines = start
		lineAll := f.fileContent[:start+1]
		//如果大于f.lineNumLimit行,则只显示后f.lineNumLimit行
		if len(lineAll) > f.lineNumLimit {
			lineAll = lineAll[len(lineAll)-f.lineNumLimit:]
		}
		join := strings.Join(lineAll, "\n")
		f.ClearScreen()
		f.PrintStdout(join)
	}
}

// 上个搜索
func (f *FileViewer) PrevSearch() {
	if len(f.searchIndex) == 0 {
		return
	}
	//找到当前索引
	for i := range f.searchIndex {
		if f.searchIndex[i] == f.currIndex {
			if i-1 < 0 {
				f.currIndex = f.searchIndex[0]
			} else {
				f.currIndex = f.searchIndex[i-1]
			}
			break
		}
	}
	//输出附近前所有行,后10行
	start := f.currIndex
	//设置翻页行数
	f.prevPageLines = start

	lineAll := f.fileContent[:start+1]
	//如果大于f.lineNumLimit行,则只显示后f.lineNumLimit行
	if len(lineAll) > f.lineNumLimit {
		lineAll = lineAll[len(lineAll)-f.lineNumLimit:]
	}
	join := strings.Join(lineAll, "\n")
	f.ClearScreen()
	f.PrintStdout(join)
}

// 下一个搜索
func (f *FileViewer) NextSearch() {
	if len(f.searchIndex) == 0 {
		return
	}
	//找到当前索引
	for i := range f.searchIndex {
		if f.searchIndex[i] == f.currIndex {
			if i+1 >= len(f.searchIndex) {
				f.currIndex = f.searchIndex[len(f.searchIndex)-1]
			} else {
				f.currIndex = f.searchIndex[i+1]
			}
			break
		}
	}
	//输出附近前所有行,后10行
	start := f.currIndex
	//设置翻页行数
	f.prevPageLines = start
	lineAll := f.fileContent[:start+1]
	//如果大于f.lineNumLimit行,则只显示后f.lineNumLimit行
	if len(lineAll) > f.lineNumLimit {
		lineAll = lineAll[len(lineAll)-f.lineNumLimit:]
	}
	join := strings.Join(lineAll, "\n")
	f.ClearScreen()
	f.PrintStdout(join)
}

// 和终端交互
func (f *FileViewer) Interactive() {

	cmd.RegisterReadStdin("file_viewer-Interactive", func(input string) {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println(err)   //错误信息
				debug.PrintStack() //错误堆栈
			}
		}()
		//暂停 ,任何操作都将会暂停
		f.pause.CompareAndSwap(false, true)
		if input == "exit" {
			f.Stop()
			cmd.UnRegisterReadStdin("file_viewer-Interactive")
			return
		}
		if input == "h" {
			fmt.Println("==============帮助文档==============")
			fmt.Println("-- h:帮助")
			fmt.Println("-- /:搜索 例如:/hello")
			fmt.Println("-- u:上一个搜索")
			fmt.Println("-- n:下一个搜索")
			fmt.Println("-- q:恢复监控")
			fmt.Println("-- w:向上翻")
			fmt.Println("-- s:向下翻")
			fmt.Println("-- p(x):设置翻页行数(默认10行) 例如:p20,如果是pn就显示所有行")
			fmt.Println("-- exit:退出")
			fmt.Println("-- clean:清屏")
			fmt.Println("-- status: 查看状态")
			fmt.Println("-- 提示:\n" +
				"1.监控缓存最多存储10万行,超过10万行会删除前面的行\n" +
				"2.任何操作都会导致文件监听暂停,可以通过q恢复监听" +
				"")
			fmt.Println("====================================")
		}
		if input == "clean" {
			f.ClearScreen()
			return
		}
		if input == "status" {
			fmt.Println("是否暂停监听:", f.pause.Load())
			fmt.Println("当前搜索关键字:", f.searchKey)
			fmt.Println("当前翻页限制行数:", f.pageLimit)
			return
		}

		if input == "q" {
			fmt.Println("恢复监控")
			//先判断队列是否为空
			if f.temp.Len() > 0 {
				//把队列中的数据输出
				for {
					if f.temp.Len() == 0 {
						break
					}
					line := f.temp.PopHead()
					f.inputChan <- line
				}
			}
			f.pause.CompareAndSwap(true, false)
		} else {
			//暂停 ,任何操作都将会暂停
			f.pause.CompareAndSwap(false, true)
		}

		if input == "w" {
			//向上翻页
			f.PrevPage()
		} else if input == "s" {
			//向下翻页
			f.NextPage()
		} else if strings.HasPrefix(input, "p") && len(input) > 1 {
			//判断是否是pn
			if input == "pn" {
				f.ClearScreen()
				join := strings.Join(f.fileContent, "\n")
				_, err := os.Stdout.WriteString(join)
				if err != nil {
					panic(err)
				}
				return
			}
			//取出数字
			pageLimit := input[1:]
			//转换为数字
			atoi, err := strconv.Atoi(pageLimit)
			if err != nil {
				return
			}
			f.pageLimit = atoi
		} else if strings.HasPrefix(input, "/") {
			//搜索
			search := input[1:]
			f.Search(search)
		} else if input == "n" {
			//下一个搜索
			f.NextSearch()
		} else if input == "u" {
			f.PrevSearch()
		}
	})

}
